import os
import git
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import calendar
from matplotlib import font_manager

# 配置中文字体
try:
    font_path = "C:/Windows/Fonts/simhei.ttf"
    font_prop = font_manager.FontProperties(fname=font_path)
    plt.rcParams['font.family'] = font_prop.get_name()
except:
    plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus'] = False

sns.set_style("whitegrid", {'font.sans-serif': ['simhei', 'Arial']})


class RepositoryAnalyzer:
    def __init__(self, repo_url, repo_name, clone_path):
        """初始化仓库分析器"""
        self.repo_url = repo_url
        self.repo_name = repo_name
        self.clone_path = clone_path
        self.visuals_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), f"visuals_{repo_name}")

        os.makedirs(self.visuals_dir, exist_ok=True)
        self.repo = self._clone_or_update_repo()

        self.commit_data = None
        self.contributors = None
        self.monthly_commits = None
        self.weekly_commits = None
        self.cumulative_commits = None
        self.lines_changed = None
        self.files_changed = None

    def _clone_or_update_repo(self):
        """克隆或更新仓库"""
        if os.path.exists(self.clone_path):
            print(f"更新仓库 {self.repo_name}...")
            repo = git.Repo(self.clone_path)
            origin = repo.remote(name='origin')
            origin.pull()
        else:
            print(f"克隆仓库 {self.repo_name}...")
            repo = git.Repo.clone_from(self.repo_url, self.clone_path)
        return repo

    def analyze_commit_data(self):
        """分析提交数据"""
        print(f"分析仓库 {self.repo_name} 的提交数据...")
        commits = list(self.repo.iter_commits('master'))

        commit_data = []
        contributors = {}

        for commit in commits:
            author = commit.author
            author_name = author.name if author else "未知作者"
            author_email = author.email if author else "unknown@example.com"
            commit_date = commit.committed_datetime
            commit_hash = commit.hexsha
            lines_added = 0
            lines_deleted = 0
            files_changed = 0

            if commit.parents:
                parent = commit.parents[0]
                diffs = self.repo.git.diff(parent.hexsha, commit.hexsha, numstat=True).split('\n')
                for diff in diffs:
                    if diff.strip():
                        parts = diff.split('\t')
                        if len(parts) >= 3:
                            added, deleted, filename = parts
                            try:
                                lines_added += int(added) if added != '-' else 0
                                lines_deleted += int(deleted) if deleted != '-' else 0
                                files_changed += 1
                            except ValueError:
                                pass
            else:
                diffs = self.repo.git.diff('--numstat', commit.hexsha).split('\n')
                for diff in diffs:
                    if diff.strip():
                        parts = diff.split('\t')
                        if len(parts) >= 3:
                            added, deleted, filename = parts
                            try:
                                lines_added += int(added) if added != '-' else 0
                                lines_deleted += int(deleted) if deleted != '-' else 0
                                files_changed += 1
                            except ValueError:
                                pass

            if author_name not in contributors:
                contributors[author_name] = {
                    'email': author_email,
                    'commits': 1,
                    'lines_added': lines_added,
                    'lines_deleted': lines_deleted,
                    'files_changed': files_changed,
                    'author': author_name
                }
            else:
                contributors[author_name]['commits'] += 1
                contributors[author_name]['lines_added'] += lines_added
                contributors[author_name]['lines_deleted'] += lines_deleted
                contributors[author_name]['files_changed'] += files_changed

            commit_data.append({
                'author': author_name,
                'date': commit_date,
                'hash': commit_hash,
                'lines_added': lines_added,
                'lines_deleted': lines_deleted,
                'files_changed': files_changed,
                'total_lines': lines_added + lines_deleted
            })

        self.commit_data = pd.DataFrame(commit_data)
        self.commit_data['date'] = pd.to_datetime(self.commit_data['date'], utc=True)
        self.contributors = sorted(contributors.values(), key=lambda x: x['commits'], reverse=True)

        self._analyze_monthly_commits()
        self._analyze_weekly_commits()
        self._analyze_cumulative_commits()
        self._analyze_changes_per_commit()

        print(f"完成仓库 {self.repo_name} 的数据收集")

    def _analyze_monthly_commits(self):
        """分析每月提交活动"""
        if self.commit_data is None or self.commit_data.empty:
            return

        # 移除时区信息
        self.commit_data['date'] = self.commit_data['date'].dt.tz_localize(None)
        self.commit_data['month'] = self.commit_data['date'].dt.strftime('%Y-%m')
        monthly = self.commit_data.groupby('month')['date'].count().sort_index(ascending=True)

        if len(monthly) > 12:
            monthly = monthly[-12:]

        self.monthly_commits = monthly

    def _analyze_weekly_commits(self):
        """分析每周提交活动"""
        if self.commit_data is None or self.commit_data.empty:
            return

        self.commit_data['weekday'] = self.commit_data['date'].dt.weekday
        weekday_names = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
        weekly = self.commit_data.groupby('weekday')['date'].count().reindex(range(7), fill_value=0)
        self.weekly_commits = {weekday_names[i]: weekly[i] for i in range(7)}

    def _analyze_cumulative_commits(self):
        """分析累计提交增长"""
        if self.commit_data is None or self.commit_data.empty:
            return

        sorted_commits = self.commit_data.sort_values('date')
        sorted_commits['cumulative'] = range(1, len(sorted_commits) + 1)
        self.cumulative_commits = sorted_commits[['date', 'cumulative']]

    def _analyze_changes_per_commit(self):
        """分析每次提交的代码变更"""
        if self.commit_data is None or self.commit_data.empty:
            return

        self.lines_changed = self.commit_data['total_lines']
        self.files_changed = self.commit_data['files_changed']

    def generate_visualizations(self):
        """生成所有可视化图表"""
        print(f"为仓库 {self.repo_name} 生成可视化图表...")

        self._generate_top_contributors_chart()
        self._generate_monthly_commits_chart()
        self._generate_weekly_commits_chart()
        self._generate_cumulative_commits_chart()
        self._generate_lines_changed_chart()
        self._generate_files_changed_chart()

        print(f"完成仓库 {self.repo_name} 的可视化图表生成")

    def _generate_top_contributors_chart(self):
        """生成前10位贡献者条形图"""
        if not self.contributors:
            return

        top_10 = self.contributors[:10]
        names = [c['author'] for c in top_10]
        counts = [c['commits'] for c in top_10]

        plt.figure(figsize=(12, 8))
        sns.barplot(x=counts, y=names, hue=names, palette="viridis", legend=False, dodge=False)
        plt.title(f"{self.repo_name} 前10位贡献者", fontsize=14, pad=20)
        plt.xlabel("提交次数", fontsize=12)
        plt.ylabel("贡献者", fontsize=12)
        plt.tight_layout(pad=3.0)

        filename = os.path.join(self.visuals_dir, f"top_contributors_{self.repo_name}.png")
        plt.savefig(filename, dpi=300, bbox_inches='tight')
        plt.close()

    def _generate_monthly_commits_chart(self):
        """生成过去12个月提交活动折线图"""
        if self.monthly_commits is None or self.monthly_commits.empty:
            return

        plt.figure(figsize=(14, 8))
        months = [str(m) for m in self.monthly_commits.index]
        counts = self.monthly_commits.values

        sns.lineplot(x=months, y=counts, marker='o', color="blue", linewidth=2.5)
        plt.title(f"{self.repo_name} 过去12个月提交活动", fontsize=14, pad=20)
        plt.xlabel("月份", fontsize=12)
        plt.ylabel("提交次数", fontsize=12)
        plt.xticks(rotation=45, ha='right')
        plt.grid(True, linestyle='--', alpha=0.6)
        plt.tight_layout(pad=3.0)

        filename = os.path.join(self.visuals_dir, f"monthly_commits_{self.repo_name}.png")
        plt.savefig(filename, dpi=300, bbox_inches='tight')
        plt.close()

    def _generate_weekly_commits_chart(self):
        """生成按星期提交活动分布条形图"""
        if self.weekly_commits is None or not self.weekly_commits:
            return

        days = list(self.weekly_commits.keys())
        counts = list(self.weekly_commits.values())

        plt.figure(figsize=(12, 8))
        sns.barplot(x=days, y=counts, hue=days, palette="pastel", legend=False, dodge=False)
        plt.title(f"{self.repo_name} 按星期提交活动分布", fontsize=14, pad=20)
        plt.xlabel("星期", fontsize=12)
        plt.ylabel("提交次数", fontsize=12)
        plt.tight_layout(pad=3.0)

        filename = os.path.join(self.visuals_dir, f"weekly_commits_{self.repo_name}.png")
        plt.savefig(filename, dpi=300, bbox_inches='tight')
        plt.close()

    def _generate_cumulative_commits_chart(self):
        """生成累计提交增长折线图"""
        if self.cumulative_commits is None or self.cumulative_commits.empty:
            return

        plt.figure(figsize=(14, 8))
        sns.lineplot(x=self.cumulative_commits['date'],
                     y=self.cumulative_commits['cumulative'],
                     color="green",
                     linewidth=2.5)
        plt.title(f"{self.repo_name} 累计提交增长", fontsize=14, pad=20)
        plt.xlabel("日期", fontsize=12)
        plt.ylabel("累计提交次数", fontsize=12)
        plt.xticks(rotation=45, ha='right')
        plt.grid(True, linestyle='--', alpha=0.6)
        plt.tight_layout(pad=3.0)

        filename = os.path.join(self.visuals_dir, f"cumulative_commits_{self.repo_name}.png")
        plt.savefig(filename, dpi=300, bbox_inches='tight')
        plt.close()

    def _generate_lines_changed_chart(self):
        """生成每次提交的代码行变更分布箱线图"""
        if self.lines_changed is None or self.lines_changed.empty:
            return

        q1, q3 = np.percentile(self.lines_changed, [25, 75])
        iqr = q3 - q1
        lower_bound = q1 - 1.5 * iqr
        upper_bound = q3 + 1.5 * iqr
        filtered_data = self.lines_changed[(self.lines_changed >= lower_bound) & (self.lines_changed <= upper_bound)]

        plt.figure(figsize=(10, 8))
        sns.boxplot(y=filtered_data, color=sns.color_palette("magma")[0])
        plt.title(f"{self.repo_name} 每次提交的代码行变更分布", fontsize=14, pad=20)
        plt.ylabel("变更行数", fontsize=12)
        plt.tight_layout(pad=3.0)

        filename = os.path.join(self.visuals_dir, f"lines_changed_{self.repo_name}.png")
        plt.savefig(filename, dpi=300, bbox_inches='tight')
        plt.close()

    def _generate_files_changed_chart(self):
        """生成每次提交的文件变更分布箱线图"""
        if self.files_changed is None or self.files_changed.empty:
            return

        q1, q3 = np.percentile(self.files_changed, [25, 75])
        iqr = q3 - q1
        lower_bound = q1 - 1.5 * iqr
        upper_bound = q3 + 1.5 * iqr
        filtered_data = self.files_changed[(self.files_changed >= lower_bound) & (self.files_changed <= upper_bound)]

        plt.figure(figsize=(10, 8))
        sns.boxplot(y=filtered_data, color=sns.color_palette("inferno")[0])
        plt.title(f"{self.repo_name} 每次提交的文件变更分布", fontsize=14, pad=20)
        plt.ylabel("变更文件数", fontsize=12)
        plt.tight_layout(pad=3.0)

        filename = os.path.join(self.visuals_dir, f"files_changed_{self.repo_name}.png")
        plt.savefig(filename, dpi=300, bbox_inches='tight')
        plt.close()

    def generate_report(self):
        """生成Markdown格式的仓库分析报告"""
        print(f"为仓库 {self.repo_name} 生成分析报告...")

        if not self.contributors or self.commit_data is None or self.commit_data.empty:
            print(f"警告: 仓库 {self.repo_name} 数据不足，无法生成完整报告")
            return

        report = f"# {self.repo_name} 仓库分析报告\n\n"
        report += f"## 仓库基本信息\n\n"
        report += f"- 仓库URL: {self.repo_url}\n"
        report += f"- 克隆路径: {self.clone_path}\n"
        report += f"- 总提交次数: {len(self.commit_data)}\n"
        report += f"- 贡献者数量: {len(self.contributors)}\n\n"

        # Add repository selection justification
        report += "## Repository Selection Justification\n\n"
        report += "We selected the PandaX repository for analysis because:\n\n"
        report += "1. It is an active IoT middleware platform with substantial development history, making it ideal for analyzing long-term development patterns.\n"
        report += "2. The project has a significant number of contributors (over 10) and commits (over 250), providing rich data for collaboration analysis.\n"
        report += "3. As an open-source project hosted on Gitee, it represents a typical Chinese-developed software project, offering insights into domestic development practices.\n"
        report += "4. The repository shows consistent activity over time, allowing us to examine development trends and contributor engagement patterns.\n\n"

        report += "## 前10位贡献者\n\n"
        report += "### 可视化图表\n\n"
        report += f"![前10位贡献者](visuals_{self.repo_name}/top_contributors_{self.repo_name}.png)\n\n"
        report += "### 详细数据\n\n"
        report += "| 贡献者 | 提交次数 |\n"
        report += "|--------|----------|\n"

        top_10 = self.contributors[:10]
        for contributor in top_10:
            report += f"| {contributor['author']} | {contributor['commits']} |\n"

        report += "\n"

        report += "## 过去12个月提交活动\n\n"
        report += "### 可视化图表\n\n"
        report += f"![过去12个月提交活动](visuals_{self.repo_name}/monthly_commits_{self.repo_name}.png)\n\n"
        report += "### 详细数据\n\n"
        if self.monthly_commits is not None and not self.monthly_commits.empty:
            report += "| 月份 | 提交次数 |\n"
            report += "|------|----------|\n"
            for month, count in self.monthly_commits.items():
                report += f"| {month} | {count} |\n"
        report += "\n"

        report += "## 按星期提交活动分布\n\n"
        report += "### 可视化图表\n\n"
        report += f"![按星期提交活动分布](visuals_{self.repo_name}/weekly_commits_{self.repo_name}.png)\n\n"
        report += "### 详细数据\n\n"
        if self.weekly_commits is not None and self.weekly_commits:
            report += "| 星期 | 提交次数 |\n"
            report += "|------|----------|\n"
            for day, count in self.weekly_commits.items():
                report += f"| {day} | {count} |\n"
        report += "\n"

        report += "## 累计提交增长\n\n"
        report += "### 可视化图表\n\n"
        report += f"![累计提交增长](visuals_{self.repo_name}/cumulative_commits_{self.repo_name}.png)\n\n"
        report += "### 数据说明\n\n"
        report += "该图表展示了仓库从创建以来的累计提交增长趋势，可以看出项目的开发活跃程度随时间的变化情况。\n\n"

        report += "## 每次提交的代码行变更分布\n\n"
        report += "### 可视化图表\n\n"
        report += f"![代码行变更分布](visuals_{self.repo_name}/lines_changed_{self.repo_name}.png)\n\n"
        report += "### 统计摘要\n\n"
        if self.lines_changed is not None and not self.lines_changed.empty:
            report += f"- 中位数: {np.median(self.lines_changed)}\n"
            report += f"- 平均值: {np.mean(self.lines_changed):.2f}\n"
            report += f"- 标准差: {np.std(self.lines_changed):.2f}\n"
            report += f"- 最小值: {np.min(self.lines_changed)}\n"
            report += f"- 最大值: {np.max(self.lines_changed)}\n"
        report += "\n"

        report += "## 每次提交的文件变更分布\n\n"
        report += "### 可视化图表\n\n"
        report += f"![文件变更分布](visuals_{self.repo_name}/files_changed_{self.repo_name}.png)\n\n"
        report += "### 统计摘要\n\n"
        if self.files_changed is not None and not self.files_changed.empty:
            report += f"- 中位数: {np.median(self.files_changed)}\n"
            report += f"- 平均值: {np.mean(self.files_changed):.2f}\n"
            report += f"- 标准差: {np.std(self.files_changed):.2f}\n"
            report += f"- 最小值: {np.min(self.files_changed)}\n"
            report += f"- 最大值: {np.max(self.files_changed)}\n"
        report += "\n"

        report_filename = f"report-{self.repo_name}.md"
        with open(report_filename, 'w', encoding='utf-8') as f:
            f.write(report)

        print(f"仓库 {self.repo_name} 分析报告已生成: {report_filename}")


def main():
    # 配置仓库信息
    repo_url = "https://gitee.com/XM-GO/PandaX"
    repo_name = "pandax"
    clone_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), repo_name)

    # 创建分析器并执行分析
    analyzer = RepositoryAnalyzer(repo_url, repo_name, clone_path)
    analyzer.analyze_commit_data()
    analyzer.generate_visualizations()
    analyzer.generate_report()


if __name__ == "__main__":
    main()